package com.simen.hellobytes;

import com.simen.hellobytes.exception.CopyFileException;
import com.simen.hellobytes.interfaces.LineIterator;

import java.io.*;

/**
 * @author Created by Simen.
 * @date 创建日期 2018/5/25 11:32
 * @modify 修改者 Simen
 */
public class IOUtil {

    public static void copyDirectory(File srcDir, File destDir) throws CopyFileException {
        copyDirectory(srcDir, destDir, false);
    }

    /**
     * @param srcDir
     * @param destDir
     * @param ignoreHiddenFile true,doesn't copy hidden file or directory;false,otherwise
     */
    public static void copyDirectory(File srcDir, File destDir, boolean ignoreHiddenFile) throws CopyFileException {
        if (!srcDir.exists() || !srcDir.isDirectory()) {
            throw new CopyFileException(srcDir.getPath() + " not exists");
        }

        File[] childFiles = srcDir.listFiles();

        if (!destDir.exists() || !destDir.isDirectory()) {
            if (!destDir.mkdirs()) {
                throw new CopyFileException(destDir.getPath() + " create directory fail");
            }
        }

        for (int i = 0; childFiles != null && i < childFiles.length; i++) {
            if (ignoreHiddenFile && childFiles[i].isHidden()) {
                continue;
            }

            if (childFiles[i].isDirectory()) {
                copyDirectory(childFiles[i], new File(destDir, childFiles[i].getName()), ignoreHiddenFile);
            } else {
                File dstFile = new File(destDir, childFiles[i].getName());

                if (dstFile.exists()) {
                    throw new CopyFileException(childFiles[i].getPath() + " already exist");
                }

                try {
                    copyFile(childFiles[i], dstFile);
                } catch (IOException e) {
                    throw new CopyFileException(childFiles[i].getPath() + " move fail", e);
                }
            }
        }
    }

    public static void copyFile(File srcFile, File destFile) throws IOException {
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;

        try {
            fileOutputStream = new FileOutputStream(destFile);
            fileInputStream = new FileInputStream(srcFile);

            copyStream(fileInputStream, fileOutputStream);
        } finally {
            closeSilently(fileInputStream);
            closeSilently(fileOutputStream);
        }
    }

    public static void copyStream(InputStream inputStream, File destFile) throws IOException {
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(destFile);

            copyStream(inputStream, fileOutputStream);
        } finally {
            closeSilently(fileOutputStream);
        }
    }

    public static void copyStream(InputStream inputStream, OutputStream outputStream) throws IOException {
        byte[] data = new byte[1024];
        int read = 0;

        while ((read = inputStream.read(data)) != -1) {
            outputStream.write(data, 0, read);
        }
    }

    public static void moveFile(File srcFile, File destFile) throws IOException {
        if (!srcFile.exists()) {
            throw new FileNotFoundException(srcFile.getAbsolutePath() + " not found");
        }

        if (!srcFile.renameTo(destFile)) {
            throw new IOException("move file " + srcFile.getAbsolutePath() + " to " + destFile.getAbsolutePath() + " fail");
        }
    }

    public static void iteratorFileLine(File file, LineIterator lineIterator) throws IOException {
        iteratorStreamLine(new FileInputStream(file), lineIterator);
    }

    public static void iteratorStreamLine(InputStream inputStream, LineIterator lineIterator) throws IOException {
        BufferedReader bufferedReader = null;
        String line = null;

        if (lineIterator == null) {
            return;
        }

        try {
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

            while ((line = bufferedReader.readLine()) != null) {
                lineIterator.onRead(line);
            }
        } finally {
            if (bufferedReader != null) {
                closeSilently(bufferedReader);
            } else {
                closeSilently(inputStream);
            }
        }
    }

    public static byte[] readFully(File file) throws IOException {
        return readStream(new FileInputStream(file));
    }

    public static byte[] readStream(InputStream is) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();

            byte[] buf = new byte[1024];
            int read;

            while ((read = is.read(buf)) != -1) {
                byteArrayOutputStream.write(buf, 0, read);
            }

            return byteArrayOutputStream.toByteArray();
        } finally {
            closeSilently(byteArrayOutputStream);
            closeSilently(is);
        }
    }

    public static byte[] readStream(InputStream is, byte[] buf) throws IOException {
        int read = is.read(buf);

        if (read > 0) {
            byte[] data = new byte[read];
            System.arraycopy(buf, 0, data, 0, read);

            return data;
        }

        throw new EOFException("read -1");
    }

    public static boolean readStreamFixByte(File file, byte[] data) {
        return readStreamFixByte(file, data, data.length);
    }

    public static boolean readStreamFixByte(File file, byte[] data, int length) {
        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
            return readStreamFixByte(inputStream, data, length);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeSilently(inputStream);
        }
        return false;
    }

    public static boolean readStreamFixByte(InputStream is, byte[] data) throws IOException {
        return readStreamFixByte(is, data, data.length);
    }

    public static boolean readStreamFixByte(InputStream is, byte[] data, int length) throws IOException {
        if (data == null || data.length == 0 || data.length < length) {
            throw new IOException("params is illegal");
        }

        int readLTotal = 0;
        int readLEvery = 0;

        while ((readLEvery = is.read(data, readLTotal, length - readLTotal)) != -1) {
            readLTotal += readLEvery;

            if (readLTotal >= length) {
                break;
            }
        }

        return readLTotal == length;
    }

    public static String readStreamContentTrimLine(InputStream is) throws IOException {
        final StringBuilder builder=new StringBuilder();
        iteratorStreamLine(is, new LineIterator() {
            @Override
            public void onRead(String line) throws IOException {
                builder.append(line.trim().replaceAll("[\\s]{2,}"," "));
            }
        });

        return builder.toString();
    }

    public static String readStreamContent(InputStream is) throws IOException {
        return readStreamContent(is, "UTF-8");
    }

    public static String readStreamContent(InputStream is, String charset) throws IOException {
        byte[] data = readStream(is);

        if (data == null) {
            return null;
        }

        return new String(data, 0, data.length, charset);
    }

    public static void readAndCloseStream(InputStream is) {
        byte[] bytes = new byte[2048];

        try {
            if (is != null) while (is.read(bytes, 0, 2048) != -1) ;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeSilently(is);
        }

    }

    public static void writeContent(byte[] content, String fileName) throws IOException {
        writeContent(content, null, fileName);
    }

    public static void writeContent(byte[] content, String fileDir, String fileName) throws IOException {
        FileOutputStream fileOutputStream = null;
        try {
            if (fileDir == null) {
                fileOutputStream = new FileOutputStream(fileName);
            } else {
                fileOutputStream = new FileOutputStream(new File(fileDir, fileName));
            }

            fileOutputStream.write(content);
            fileOutputStream.flush();
        } finally {
            closeSilently(fileOutputStream);
        }
    }

    public static void closeSilently(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    public static void printStreamContent(InputStream is) throws IOException {
        byte[] buf = new byte[1024];
        int read = 0;

        while ((read = is.read(buf)) != -1) {
            System.out.println(new String(buf, 0, read));
        }

    }

    public static void printErrStreamContent(InputStream is) throws IOException {
        byte[] buf = new byte[1024];
        int read = 0;

        while ((read = is.read(buf)) != -1) {
            System.err.println(new String(buf, 0, read));
        }

    }

    public static void printStreamContent(InputStream is, String tag) throws IOException {
        byte[] buf = new byte[1024];
        int read = 0;

        while ((read = is.read(buf)) != -1) {
            System.out.println(new String(buf, 0, read));
        }

    }

    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T cloneObj(T obj) throws IOException, ClassNotFoundException {
        T cloneObj = null;
        ByteArrayOutputStream out = null;
        ObjectOutputStream objOStream = null;

        ByteArrayInputStream baIStream = null;
        ObjectInputStream objIStream = null;

        try {
            out = new ByteArrayOutputStream();
            objOStream = new ObjectOutputStream(out);

            objOStream.writeObject(obj);

            baIStream = new ByteArrayInputStream(out.toByteArray());
            objIStream = new ObjectInputStream(baIStream);

            cloneObj = (T) objIStream.readObject();
        } finally {
            IOUtil.closeSilently(objOStream);
            IOUtil.closeSilently(objOStream);
        }

        return cloneObj;
    }

}
